home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
AMIGA
/
AMICUS
/
AMICUS26.ADF
/
SoundScape
/
AztecExamples
/
tx.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-01-26
|
13KB
|
463 lines
/* TX.C
(c) 1987 Todor Fay
*/
#include "exec/types.h"
#include "exec/exec.h"
#include "hardware/custom.h"
#include "hardware/cia.h"
#include "exec/interrupts.h"
#include "exec/memory.h"
#include "hardware/intbits.h"
#include "libraries/dos.h"
#include "intuition/intuition.h"
#include "soundscape.h"
extern struct CIA ciaa;
extern struct Custom custom;
/* The state structure for this module has just the
file name for saving a TX81Z (or DX21/27/100) voice.
Whenever an environment save command (SAVESTATE)
occurs, the file that the voice was saved in is
returned here. When an environment load command
(LOADSTATE) happens, the file that is given here
will be loaded by this module.
*/
struct SynthState {
long length;
char filename[70];
};
/* Initialise the file name as empty. */
static struct SynthState synthstate = { 70,0, };
/* The port id for this port. */
static short thisport;
/* The icon in the Patch Panel. */
UWORD tx81zdata[] = { /* 32 x 12 */
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
8078, 32752,
12486, 448,
8070, 1792,
12486, 7168,
8079, 32752,
16383, 57344,
224, 0,
231, 28672,
227, 57344,
225, 49152,
227, 57344,
231, 28672,
0, 32752,
0, 448,
0, 1792,
0, 7168,
0, 32752,
};
struct Image tx81zimage = { 0,0,32,12,2,tx81zdata,3,0,0 };
/* We need to be able to display messages to the user.
For example, "Sending the voice...". Two
routines handle this. Onmessage opens a window
and displays the given strings. Offmessage
closes that window. Here is the window declaration.
*/
struct NewWindow NewWindowStructure = {
134,35,
336,66,
0,1,
NULL,
ACTIVATE,
NULL,
NULL,
"TX81Z/DX21/DX27/DX100 Librarian",
NULL,
NULL,
5,5,
640,200,
WBENCHSCREEN
};
struct Window *messagewindow = 0;
void onmessage(string1,string2)
/* Opens a window and prints the message.
Window is closed by offmessage().
*/
char string1[], string2[];
{
static struct IntuiText text = { 2,0,JAM2,0,0,0,0, };
messagewindow = (struct Window *)
OpenWindow(&NewWindowStructure);
if (messagewindow) {
text.IText = string1;
PrintIText(messagewindow->RPort,&text,20,20);
if (string2) {
text.IText = string2;
PrintIText(messagewindow->RPort,&text,20,30);
}
}
}
void offmessage()
/* This just closes messagewindow if it is open. */
{
if (messagewindow) {
CloseWindow(messagewindow);
}
messagewindow = 0;
}
opencode(direction)
/* Routine to open this modules's port in the
direction specified. Always return TRUE
for success.
*/
char direction;
{
return(1);
}
closecode(direction)
/* Always close successfully. */
char direction;
{
return(1);
}
unsigned long processbyte(buffer,maxlength,midiindata)
/* When a data byte comes in, this routine is
called to process it. Buffer is a pointer
to an array to store the data in. Maxlength
is the size of that array. Midiindata is
the data byte that just came in.
The static variable sysexon keeps track of
whether we are currently reading a system
exclusive packet. It is initialised to
FALSE (0). When a System Exclusive status
byte comes in, sysexon is set. When the
end of the packet is reached, sysexon is
turned off again.
The static variable length keeps track of
where we are in the array. It is not
allowed to become greater than maxlength.
Return 0 if still reading or about to
read the packet. Return the length when
the packet end has been reached.
*/
register unsigned char *buffer;
register unsigned long maxlength;
register unsigned char midiindata;
{
static unsigned long length;
static char sysexon = 0;
/* Is this a status byte? */
if (midiindata & 0x80) {
/* MIDI real time event? */
if (midiindata >= CLOCK) {
return(0);
}
/* End of packet? */
else if (sysexon) {
sysexon = 0;
buffer[length++] = midiindata;
return(length);
}
/* Start of packet? */
else if (midiindata == SYSTEMX) {
sysexon = 1;
length = 1;
*buffer = SYSTEMX;
}
}
/* Read data. */
else if (sysexon) {
if (length < maxlength) {
buffer[length++] = midiindata;
}
}
return(0);
}
unsigned long ReceiveSysEx(buffer,maxlength)
/* To receive a system exclusive buffer,
disable interrupts and poll the serial
port and mouse button. Whenever a data
byte comes in, pass it to the processbyte
routine, which deals with is appropriately.
Once the packet is in, return the length.
*/
register char *buffer;
register unsigned long maxlength;
{
register unsigned char c;
unsigned long length = 0;
Disable();
for (;;) {
while (!(custom.serdatr & 0x4000)) {
if ((~ciaa.ciapra) & 0xC0) {
Enable();
return(0);
}
}
c = (unsigned char) custom.serdatr;
custom.intreq = INTF_RBF;
if (length = processbyte(buffer,maxlength,c))
break;
}
Enable();
return(length);
}
void SendSysEx(buffer,length)
/* First, set the priority of this task to
higher than the SoundScape packet router
so there is no danger of SoundScape sending
a MIDI packet at the same time.
Then, send all the bytes in the buffer,
polling the buffer empty bit before
sending each byte.
When done, bring the priority back down.
*/
register char *buffer;
register unsigned long length;
{
register long oldpri = SetTaskPri(FindTask(0),21);
register unsigned long i;
for (i=0; i<length; i++) {
while (!(custom.serdatr & 0x2000));
custom.serdat = buffer[i] | 0x300;
}
SetTaskPri(FindTask(0),oldpri);
}
void dosomething(direction,filename)
/* This routine is called to either read
a voice from disk and send it
to the synth, or read a voice from
the synth and store it to disk.
Direction is set if we are to
store a sound to disk, otherwise cleared.
A filename may be given (the case for
environment loads and saves.) If
so, the variable filename will have
that file name in it. If there is
no filename (filename[0] == 0),
put up a requester with a call to
ReadFileName or WriteFileName.
If saving a voice to disk, first
send a dump request to the synth.
Then read the system exclusive packet
that comes back. There is a danger
that no synth is hooked up, or the
wrong data is returned. If this happens,
the length of the data returned will not
be 101, so don't save it to file.
If sending a new voice, just read
it from file and send the data.
*/
char direction;
register char filename[];
{
register long buffer;
register long file;
static unsigned char dumprequest[] = { 0xF0,0x43,0x20,0x03,0xF7};
long length;
buffer = AllocMem(200,MEMF_PUBLIC);
if (buffer) {
if (direction) {
if (!filename[0])
WriteFileName(filename,"TX81Z/DX Voice","tx81zvoice");
if (filename[0]) {
onmessage("Getting a Voice... ",
"Click mouse to abort");
SendSysEx(dumprequest,5);
length = ReceiveSysEx(buffer,200);
offmessage();
if (length == 101) {
file = Open(filename,MODE_NEWFILE);
if (file) {
Write(file,&length,4);
Write(file,buffer,length);
Close(file);
}
}
}
}
else {
if (!filename[0])
ReadFileName(filename,"TX81Z/DX Voice","tx81zvoice");
if (filename[0]) {
file = Open(filename,MODE_OLDFILE);
if (file) {
Read(file,&length,4);
if (length == 101) {
Read(file,buffer,length);
Close(file);
onmessage("Sending the Voice... ",0);
SendSysEx(buffer,length);
offmessage();
}
}
}
}
FreeMem(buffer,200);
}
}
void editcode(direction,command,buffer)
/* This, the edit routine, is called under
five circumstances:
USEREDIT:
The user clicked on either the left or right
icon in the patch panel. Which icon is
determined by the variable direction. Clear
the SynthState filename field so there is no
current filename and make a FunctionCall to
dosomething(direction,synthstate.filename)
which will either load a sound or save
one.
SETSTATE:
This occurs when another module wishes to
change the data in this module's state
structure. Simply copy the passed buffer
into synthstate.
GETSTATE:
Another module wishes to read the contents
of this module's state structure. Simply
copy synthstate into the passed buffer.
LOADSTATE:
Another module asks this module to load
files. This ususally occurs in the
context of an environment load. Once again,
a buffer is passed and the data from it
is copied into synthstate. This should have the
name of the file to read and send to the
TX81Z.
SAVESTATE:
Another module asks this module to save
files. This usually occurs in the
context of an environment save. This
time, we get the sound from the TX81Z and
save it to disk, then return the file name,
so that can be stored in the environment
file.
*/
char direction, command;
struct SynthState *buffer;
{
enteraztec();
switch (command) {
case USEREDIT :
synthstate.filename[0] = 0;
FunctionCall(dosomething,direction,synthstate.filename);
break;
case SETSTATE :
movmem(buffer,&synthstate,sizeof(synthstate));
break;
case GETSTATE :
movmem(&synthstate,buffer,sizeof(synthstate));
break;
case LOADSTATE :
movmem(buffer,&synthstate,sizeof(synthstate));
FunctionCall(dosomething,0,synthstate.filename);
movmem(&synthstate,buffer,sizeof(synthstate));
break;
case SAVESTATE :
FunctionCall(dosomething,1,synthstate.filename);
movmem(&synthstate,buffer,sizeof(synthstate));
break;
}
buffer->length = 70;
leaveaztec();
}
long SoundScapeBase, IntuitionBase, GfxBase;
/* Here's the main program. As always with a
SoundScape module, open the SoundScape library
to get a handle on the routines, then close
it so it can be eventually closed by the
program that opened it initially.
Create a midi port in the patch panel and
wait for it to be closed, then leave.
*/
void main() {
IntuitionBase = OpenLibrary("intuition.library",0);
GfxBase = OpenLibrary("graphics.library",0);
SoundScapeBase = OpenLibrary("soundscape.library",0);
if (SoundScapeBase) {
CloseLibrary(SoundScapeBase);
thisport = AddMidiPort(opencode,closecode,editcode,0,
&tx81zimage,&tx81zimage,-1,"tx81z");
SetTaskPri(FindTask(0),-20);
while (MidiPort(thisport)) Delay(100);
}
CloseLibrary(IntuitionBase);
CloseLibrary(GfxBase);
}